from datetime import datetime
from random import random
from string import Template


class mapper():
	def __init__(self, config):
		self._last_values = []
		self._config = config
		self._seconds = 0
		self._timers = [self._create_timer(timer) for timer in config['_timers']]

	def map(self, values, mappings, mapped_values):
		# print(values)
		for value in values:
			is_new = False
			if value in self._last_values:
				is_new = False
			else:
				is_new = True

			self._map(value, is_new, mappings, mapped_values)

		self._check_timers(mapped_values)
		self._check_conditions(mapped_values)
		self._last_values = values

	def _map(self, value, is_new, mappings, mapped_values):
		other_actions = self._config.get(value)
		if other_actions is not None:
			for action in other_actions:
				self._do_other_action(action, is_new, mapped_values)

		selectable_mappings = [mapping for mapping in mappings if mapping['input'] == value]
		for selectable_mapping in selectable_mappings:
			mapped_values[selectable_mapping['output']['name']] = selectable_mapping['output']['state']

	def _do_other_action(self, action, is_new, mapped_values):
		# if is_new is True:
		# 	print('new action ' + str(action))
		for action_method in action:
			action_args = action[action_method]
			if action_method == 'timer':
				self._method_timer(is_new, action_args)
			elif action_method == 'seconds':
				self._method_seconds(is_new, action_args)
			elif action_method == 'state':
				self._method_state(is_new, action_args, mapped_values)

	def _create_timer(self, timer):
		return {
			'name': timer['name'],
			'seconds': timer['min'],
			'end': timer['on_end'],
			'counter': 0,
			'start': datetime.utcnow(),
			'max': timer['max'],
			'min': timer['min'],
		}

	def _check_timers(self, mapped_values):
		now = datetime.utcnow()
		for timer in self._timers:
			counter = timer['counter']

			start = timer['start']
			seconds = timer['seconds']

			passed_seconds = (now - start).total_seconds()

			if passed_seconds > seconds:
				if counter > 0:
					self._trigger_timer(timer, mapped_values)
				else:
					timer['seconds'] = timer['min']

	def _check_conditions(self, mapped_values):
		pass

	def _trigger_timer(self, timer, mapped_values):
		timer['counter'] -= 1
		timer['start'] = datetime.utcnow()
		ends = timer['end']

		self._set_weighted_state(ends, mapped_values)

	def _get_timer_from_name(self, name):
		return [timer for timer in self._timers if timer['name'] == name][0]

	def _get_flat_timers(self):
		flat_timers = {}
		for timer in self._timers:
			flat_timers[timer['name'] + '_seconds'] = timer['seconds']
			flat_timers[timer['name'] + '_counter'] = timer['counter']

		return flat_timers

	def _set_weighted_state(self, states, mapped_values):
		max_weight = sum(state['weight'] for state in states)
		weight_random = random() * max_weight
		flat_timers = self._get_flat_timers()
		# print(flat_timers)

		for state in states:
			weight_random -= state['weight']
			if weight_random <= 0:
				template = Template(state['state'])
				new_state = template.safe_substitute(flat_timers)
				mapped_values[state['name']] = new_state
				return

	def _method_state(self, is_new, action_args, mapped_values):
		if is_new is False:
			return

		self._set_weighted_state(action_args, mapped_values)

	def _method_timer(self, is_new, action_args):
		action = action_args['action']
		timer = self._get_timer_from_name(action_args['name'])
		counter = timer['counter']

		if action == 'restart':
			if is_new is True:
				timer['counter'] = counter + 1

			if timer['counter'] == 0:
				return

			timer['start'] = datetime.utcnow()

	def _method_seconds(self, is_new, action_args):
		action = action_args['action']
		timer = self._get_timer_from_name(action_args['name'])

		if is_new is False:
			return

		if action == 'add':
			value = action_args['value']
			seconds = timer['seconds']
			seconds += value

		if action == 'subtract':
			value = action_args['value']
			seconds = timer['seconds']
			seconds -= value

		if seconds > timer['max']:
			seconds = timer['max']
		elif seconds < timer['min']:
			seconds = timer['min']

		timer['seconds'] = seconds
